package http

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"time"
)

type Client struct {
	Url          string
	Headers      map[string]string
	StatusCode   int
	Cookies      []*http.Cookie
	RequestData  []byte
	ResponseData []byte
	RetryTimes   int
}

func NewClient(url string) *Client {
	c := &Client{Url: url}
	c.Headers = make(map[string]string)
	c.Cookies = make([]*http.Cookie, 0)
	c.RetryTimes = 3 // default 3 times

	return c
}

func (c *Client) Get() error {
	return c.RequestWithRetry("GET")
}

func (c *Client) Post() error {
	return c.RequestWithRetry("POST")
}

func (c *Client) RequestWithRetry(method string) error {
	if c.RetryTimes < 0 {
		c.RetryTimes = 0
	}

	var retries = c.RetryTimes
	for {
		if retries == 0 {
			return c.Request(method)
		}

		err := c.Request(method)
		if err != nil {
			retries--
			log.Printf("will retry request...[%d/%d]\n", c.RetryTimes-retries, c.RetryTimes)
			time.Sleep(500 * time.Millisecond)
		} else {
			return nil
		}
	}
}

func (c *Client) Request(method string) error {
	// clean lastest response
	c.StatusCode = -1
	c.ResponseData = nil

	// set cookie
	jar, err := cookiejar.New(nil)
	if err != nil {
		return err
	}

	u, err := url.Parse(c.Url)
	if err != nil {
		return err

	}
	jar.SetCookies(u, c.Cookies)

	// set request
	req, err := http.NewRequest(method, c.Url, bytes.NewBuffer(c.RequestData))
	if err != nil {
		return err
	}
	for k, v := range c.Headers {
		req.Header.Add(k, v)
	}

	// send request
	httpClient := &http.Client{Jar: jar}
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	c.StatusCode = resp.StatusCode
	c.ResponseData, err = ioutil.ReadAll(resp.Body)
	return err
}
